# TriggerSwitchD, executes commands to activate/deactive remote switches.
# (c) Copyright 2010 Pontus Strömdahl, AdhocSkill. 
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

require 'socket'
require 'yaml'
require 'logger'
require 'ipc'


module TriggerSwitchD

  #starts an instance of the daemon.
  class Application    
    attr_reader :devices, :schedule, :logger
    alias_method :list_devices, :devices
    
    #Prepares the run by loading up the environment
    #located in config/config.rb
    def initialize(config_file)
      @current_date = Date.new
      ConfigFactory.new(config_file)
      @logger = Logger.new(Config.log_object)
      logger.info("Environment prepared")
    end
    
    ANYTHING_STR = "anything"
    RESPONSE_STR = "response"
    RECEIVED_STR = "received"
    #Runs the daemon untill process killed or it receives
    #stop from client connected via unix socket
    def start
      @ipc = IPC.create(Config.unix_socket_path)
      @logger.info("IPC started")
      __run_loop__
      @logger.info("Received stop via IPC")
    end
    
    def list_schedule #:nodoc:
      schedule.by_name.keys
    end

    private
    
    def load_db_at(time)
      return unless did_set_current_date?(time)
      load_db
      logger.info("Loaded #{devices.length} devices and #{schedule.by_name.length} actions")
      Config::flush_log
    end
    
    def did_set_current_date?(time)
      date = Date.parse(time.strftime("%Y-%m-%d %H:%M:%S"))
      set_current_date = (@current_date != date)
      @current_date = date if set_current_date
      set_current_date
    end
    
    def load_db
      @schedule = Schedule.create
      Application.load_db_file(:devices) { |file| @devices = TriggerSwitchD::load_devices(file) }
      inner_block = Proc.new do |code_line, date| 
        @schedule.schedule_action(Action.new(code_line,devices,Config.environment,date))
      end
      Application.load_db_file(:schedule) do |file| 
        TriggerSwitchD::load_scheduled_actions(file,&inner_block) 
      end
    end
    
    def self.load_db_file(filename_symbol)
      lookup = {:devices => 0, :schedule => 1}
      file = File.open(Config.db_path[lookup[filename_symbol]],"r")
      yield(file)
      file.close
    end
    
    def received
      result = @ipc[RECEIVED_STR]
      return result if result != ""
      reset_received
      ANYTHING_STR
    end
    
    def reset_received
      @ipc[RECEIVED_STR] = ANYTHING_STR
    end
    
    def __run_loop__
      command = ANYTHING_STR
      while ((command = received).to_sym != :stop)
        __inner_run_loop__(command)
        Kernel.sleep(1)
      end
    end
    
    def __inner_run_loop__(command)
      now_time = Time.new
      load_db_at(now_time)
      __respond_to_ipc__(command) if methods.include? command
      __execute_action__(now_time.strftime("%H:%M"))
    end
    
    def __respond_to_ipc__(command)
      @ipc[RESPONSE_STR] = send(command).to_yaml
      logger.info("Received #{command} via IPC")
      Config::flush_log
      reset_received
      @ipc.run
    end
    
    def __execute_action__(hhmm_now_time)
      cronolog = @schedule.cronological
      return unless (cronolog.keys.include? hhmm_now_time)
      logger.info("Executed #{cronolog[hhmm_now_time].length} action(s) at #{hhmm_now_time}")
      Config::flush_log
      @schedule.execute(hhmm_now_time,Config.output)
    end
        
  end
end
